#include "PlotZoomer.h"
#include "ZoomData.h"

PlotZoomer::PlotZoomer(QWidget *canvas):
    QwtPlotZoomer(canvas),
    _getCornerWidget(NULL),
    _horizontalScrollData(NULL),
    _verticalScrollData(NULL),
    _inZoom(false)
{
    for (int axis = 0; axis < QwtPlot::axisCnt; axis++)
        _alignCanvasToScales[axis] = false;

    if (!canvas)
        return;

    _horizontalScrollData = new ZoomData();
    _verticalScrollData = new ZoomData();
}

PlotZoomer::~PlotZoomer()
{
    delete _getCornerWidget;
    delete _verticalScrollData;
    delete _horizontalScrollData;
}

void PlotZoomer::rescale()
{
    QwtScaleWidget *xScale = plot()->axisWidget(xAxis());
    QwtScaleWidget *yScale = plot()->axisWidget(yAxis());

    if (zoomRectIndex() <= 0)
    {
        if (_inZoom)
        {
            xScale->setMinBorderDist(0, 0);
            yScale->setMinBorderDist(0, 0);

            QwtPlotLayout *layout = plot()->plotLayout();

            for (int axis = 0; axis < QwtPlot::axisCnt; axis++)
                layout->setAlignCanvasToScale(axis, _alignCanvasToScales);

            _inZoom = false;
        }
    }
    else
    {
        if (!_inZoom)
        {
            int start, end;

            xScale->getBorderDistHint(start, end);
            xScale->setMinBorderDist(start, end);

            yScale->getBorderDistHint(start, end);
            yScale->setMinBorderDist(start, end);

            QwtPlotLayout *layout = plot()->plotLayout();
            for (int axis = 0; axis < QwtPlot::axisCnt; axis++)
            {
                _alignCanvasToScales[axis] = layout->alignCanvasToScale(axis);
            }

            layout->setAlignCanvasToScales(false);

            _inZoom = true;
        }
    }

    QwtPlotZoomer::rescale();
    updateScrollBars();
}

ZoomScrollBar *PlotZoomer::scrollBar(Qt::Orientation orientation)
{
    ZoomScrollBar *&sb = (orientation == Qt::Vertical) ? _verticalScrollData->scrollBar : _horizontalScrollData->scrollBar;

    if (sb == NULL)
    {
        sb = new ZoomScrollBar(orientation, canvas());
        sb->hide();
        connect(sb, SIGNAL(valueChanged(Qt::Orientation, double, double)),
            SLOT(scrollBarMoved(Qt::Orientation, double, double)));
    }
    return sb;
}

ZoomScrollBar *PlotZoomer::horizontalScrollBar() const
{
    return _horizontalScrollData->scrollBar;
}

ZoomScrollBar *PlotZoomer::verticalScrollBar() const
{
    return _verticalScrollData->scrollBar;
}

void PlotZoomer::setHorizontalScrollBarMode(Qt::ScrollBarPolicy mode)
{
    if (getHorizontalScrollBarMode() != mode)
    {
        _horizontalScrollData->mode = mode;
        updateScrollBars();
    }
}

void PlotZoomer::setVerticalScrollBarMode(Qt::ScrollBarPolicy mode)
{
    if (getVerticalScrollBarMode() != mode)
    {
        _verticalScrollData->mode = mode;
        updateScrollBars();
    }
}

Qt::ScrollBarPolicy PlotZoomer::getHorizontalScrollBarMode() const
{
    return _horizontalScrollData->mode;
}

Qt::ScrollBarPolicy PlotZoomer::getVerticalScrollBarMode() const
{
    return _verticalScrollData->mode;
}

void PlotZoomer::setHorizontalScrollBarPosition(ScrollBarPosition pos)
{
    if (_horizontalScrollData->position != pos)
    {
        _horizontalScrollData->position = pos;
        updateScrollBars();
    }
}

void PlotZoomer::setVerticalScrollBarPosition(ScrollBarPosition pos)
{
    if (_verticalScrollData->position != pos)
    {
        _verticalScrollData->position = pos;
        updateScrollBars();
    }
}

PlotZoomer::ScrollBarPosition PlotZoomer::getHorizontalScrollBarPosition() const
{
    return _horizontalScrollData->position;
}

PlotZoomer::ScrollBarPosition PlotZoomer::getVerticalScrollBarPosition() const
{
    return _verticalScrollData->position;
}

void PlotZoomer::setCornerWidget(QWidget *w)
{
    if (w != _getCornerWidget)
    {
        if (canvas())
        {
            delete _getCornerWidget;
            _getCornerWidget = w;
            if (_getCornerWidget->parent() != canvas())
                _getCornerWidget->setParent(canvas());

            updateScrollBars();
        }
    }
}

QWidget *PlotZoomer::getCornerWidget() const
{
    return _getCornerWidget;
}

bool PlotZoomer::eventFilter(QObject *object, QEvent *event)
{
    if (object == canvas())
    {
        switch(event->type())
        {
            case QEvent::Resize:
            {
                int left, top, right, bottom;
                canvas()->getContentsMargins(&left, &top, &right, &bottom);

                QRect rect;
                rect.setSize(static_cast<QResizeEvent *>(event)->size());
                rect.adjust(left, top, -right, -bottom);

                layoutScrollBars(rect);
                break;
            }
            case QEvent::ChildRemoved:
            {
                const QObject *child =
                    static_cast<QChildEvent *>(event)->child();

                if (child == _getCornerWidget)
                    _getCornerWidget = NULL;
                else if (child == _horizontalScrollData->scrollBar)
                    _horizontalScrollData->scrollBar = NULL;
                else if (child == _verticalScrollData->scrollBar)
                    _verticalScrollData->scrollBar = NULL;
                break;
            }
            default:
                break;
        }
    }
    return QwtPlotZoomer::eventFilter(object, event);
}

bool PlotZoomer::needScrollBar(Qt::Orientation orientation) const
{
    Qt::ScrollBarPolicy mode;
    double zoomMin, zoomMax, baseMin, baseMax;

    if (orientation == Qt::Horizontal)
    {
        mode = _horizontalScrollData->mode;
        baseMin = zoomBase().left();
        baseMax = zoomBase().right();
        zoomMin = zoomRect().left();
        zoomMax = zoomRect().right();
    }
    else
    {
        mode = _verticalScrollData->mode;
        baseMin = zoomBase().top();
        baseMax = zoomBase().bottom();
        zoomMin = zoomRect().top();
        zoomMax = zoomRect().bottom();
    }

    bool needed = false;
    switch(mode)
    {
        case Qt::ScrollBarAlwaysOn:
            needed = true;
            break;
        case Qt::ScrollBarAlwaysOff:
            needed = false;
            break;
        default:
        {
            if (baseMin < zoomMin || baseMax > zoomMax)
                needed = true;
            break;
        }
    }
    return needed;
}

void PlotZoomer::updateScrollBars()
{
    if (!canvas())
        return;

    const int xAxis = QwtPlotZoomer::xAxis();
    const int yAxis = QwtPlotZoomer::yAxis();

    int xScrollBarAxis = xAxis;
    if (getHorizontalScrollBarPosition() == OppositeToScale)
        xScrollBarAxis = oppositeAxis(xScrollBarAxis);

    int yScrollBarAxis = yAxis;
    if (getVerticalScrollBarPosition() == OppositeToScale)
        yScrollBarAxis = oppositeAxis(yScrollBarAxis);


    QwtPlotLayout *layout = plot()->plotLayout();

    bool showHScrollBar = needScrollBar(Qt::Horizontal);
    if (showHScrollBar)
    {
        ZoomScrollBar *sb = scrollBar(Qt::Horizontal);
        sb->setPalette(plot()->palette());
        sb->setInverted(!plot()->axisScaleDiv(xAxis).isIncreasing());
        sb->setBase(zoomBase().left(), zoomBase().right());
        sb->moveSlider(zoomRect().left(), zoomRect().right());

        if (!sb->isVisibleTo(canvas()))
        {
            sb->show();
            layout->setCanvasMargin(layout->canvasMargin(xScrollBarAxis) + sb->extent(), xScrollBarAxis);
        }
    }
    else
    {
        if (horizontalScrollBar())
        {
            horizontalScrollBar()->hide();
            layout->setCanvasMargin(layout->canvasMargin(xScrollBarAxis) - horizontalScrollBar()->extent(), xScrollBarAxis);
        }
    }

    bool showVScrollBar = needScrollBar(Qt::Vertical);
   
	if (showVScrollBar)
    {
        ZoomScrollBar *sb = scrollBar(Qt::Vertical);
        sb->setPalette(plot()->palette());
        sb->setInverted(!plot()->axisScaleDiv(yAxis).isIncreasing());
        sb->setBase(zoomBase().top(), zoomBase().bottom());
        sb->moveSlider(zoomRect().top(), zoomRect().bottom());

        if (!sb->isVisibleTo(canvas()))
        {
            sb->show();
            layout->setCanvasMargin(layout->canvasMargin(yScrollBarAxis) + sb->extent(), yScrollBarAxis);
        }
    }
    else
    {
        if (verticalScrollBar())
        {
            verticalScrollBar()->hide();
            layout->setCanvasMargin(layout->canvasMargin(yScrollBarAxis) - verticalScrollBar()->extent(), yScrollBarAxis);
        }
    }

    if (showHScrollBar && showVScrollBar)
    {
        if (_getCornerWidget == NULL)
        {
            _getCornerWidget = new QWidget(canvas());
            _getCornerWidget->setAutoFillBackground(true);
            _getCornerWidget->setPalette(plot()->palette());
        }
        _getCornerWidget->show();
    }
    else
    {
        if (_getCornerWidget)
            _getCornerWidget->hide();
    }

    layoutScrollBars(canvas()->contentsRect());
    plot()->updateLayout();
}

void PlotZoomer::layoutScrollBars(const QRect &rect)
{
    int hPos = xAxis();
    if (getHorizontalScrollBarPosition() == OppositeToScale)
        hPos = oppositeAxis(hPos);

    int vPos = yAxis();
    if (getVerticalScrollBarPosition() == OppositeToScale)
        vPos = oppositeAxis(vPos);

    ZoomScrollBar *hScrollBar = horizontalScrollBar();
    ZoomScrollBar *vScrollBar = verticalScrollBar();

    const int hdim = hScrollBar ? hScrollBar->extent() : 0;
    const int vdim = vScrollBar ? vScrollBar->extent() : 0;

    if (hScrollBar && hScrollBar->isVisible())
    {
        int x = rect.x();
        int y = (hPos == QwtPlot::xTop)
            ? rect.top() : rect.bottom() - hdim + 1;
        int w = rect.width();

        if (vScrollBar && vScrollBar->isVisible())
        {
            if (vPos == QwtPlot::yLeft)
                x += vdim;
            w -= vdim;
        }

        hScrollBar->setGeometry(x, y, w, hdim);
    }
    if (vScrollBar && vScrollBar->isVisible())
    {
        int pos = yAxis();
        if (getVerticalScrollBarPosition() == OppositeToScale)
            pos = oppositeAxis(pos);

        int x = (vPos == QwtPlot::yLeft) ? rect.left() : rect.right() - vdim + 1;
        int y = rect.y();

        int h = rect.height();

        if(hScrollBar && hScrollBar->isVisible())
        {
            if (hPos == QwtPlot::xTop)
                y += hdim;

            h -= hdim;
        }

        vScrollBar->setGeometry(x, y, vdim, h);
    }
    if (hScrollBar && hScrollBar->isVisible() && vScrollBar && vScrollBar->isVisible())
    {
        if (_getCornerWidget)
        {
            QRect cornerRect(vScrollBar->pos().x(), hScrollBar->pos().y(), vdim, hdim);
            _getCornerWidget->setGeometry(cornerRect);
        }
    }
}

void PlotZoomer::scrollBarMoved(Qt::Orientation o, double min, double max)
{
    Q_UNUSED(max);

    if (o == Qt::Horizontal)
        moveTo(QPointF(min, zoomRect().top()));
    else
        moveTo(QPointF(zoomRect().left(), min));

    emit zoomed(zoomRect());
}

int PlotZoomer::oppositeAxis(int axis) const
{
    switch(axis)
    {
        case QwtPlot::xBottom:
            return QwtPlot::xTop;
        case QwtPlot::xTop:
            return QwtPlot::xBottom;
        case QwtPlot::yLeft:
            return QwtPlot::yRight;
        case QwtPlot::yRight:
            return QwtPlot::yLeft;
        default:
            break;
    }
    return axis;
}
